import { lcs_node, lcs_node_prop_child_node_arr } from './cla_lcs_node';
import { ObjectOnlyPushSet } from '../../../../ObjectOnlyPushSet_operate';
import { toStringTypeCheck } from '../../../../toStringTypeCheck';
import { lock_class_func } from '../../../../def';

const all_lcs_nodeTag = 'all_lcs_node';

const all_lcs_node_prop_all_node = 'all_node';

class all_lcs_node {
  get [Symbol.toStringTag]() {
    return all_lcs_nodeTag;
  }

  /**
   * 默认 按次序 找寻 最长的 子链
   * @param {[]} x_arr 主 判定 元素组
   * @param {[]} y_arr 次 判定 元素组
   * @param {function} [value_same_cb = undefined] 值 判定 - 创建 节点 函数
   * @param {function} [crease_node_cb = undefined] 节点 判定 - 扩增 子节点
   * @param {function} [xOry_cb = undefined] 默认 (true) 采用 x_arr 的 值 作为 节点 对象值
   */
  constructor(
    x_arr,
    y_arr,
    value_same_cb = undefined,
    crease_node_cb = undefined,
    xOry_cb = undefined
  ) {
    if (toStringTypeCheck(value_same_cb, 'function')) {
      this.value_same_cb = value_same_cb;
    }

    if (toStringTypeCheck(crease_node_cb, 'function')) {
      this.crease_node_cb = crease_node_cb;
    }

    if (toStringTypeCheck(xOry_cb, 'function')) {
      this.xOry_cb = xOry_cb;
    }

    this.source_x = x_arr;

    this.source_y = y_arr;

    // 锁定 start
    lock_class_func(this, 'start');
  }

  source_x;

  source_y;

  start() {
    let x_val;
    let y_val;

    for (let x_ind = this.source_x.length - 1; x_ind >= 0; x_ind--) {
      x_val = this.source_x[x_ind];
      for (let y_ind = this.source_y.length - 1; y_ind >= 0; y_ind--) {
        y_val = this.source_y[y_ind];
        if (!this.value_same_cb(x_val, y_val)) {
          continue;
        }

        ObjectOnlyPushSet(
          this,
          this.#get_xind_yind(x_ind, y_ind),
          new lcs_node(x_ind, y_ind, this.xOry_cb(x_val, y_val)),
          all_lcs_node_prop_all_node
        );

        this.#crease_cur_node(x_ind, y_ind);
      }
    }
    this.longest_length = this.#get_longest_length();

    this.longest_squence_arr = this.#get_longest_squence_arr();

    return this;
  }

  /**
   * 节点 信息 集 坐标
   * @type {`${Number},${Number}`[]}
   */
  [all_lcs_node_prop_all_node] = [];

  #get_xind_yind(x_ind, y_ind) {
    return `${x_ind},${y_ind}`;
  }

  /**
   * 对 当前 节点 扩增 后续 正确的 节点 链
   * @param {number} x_ind 主 序列
   * @param {number} y_ind
   */
  #crease_cur_node(x_ind, y_ind) {
    /**
     * @type {lcs_node}
     */
    let cur_node = this[this.#get_xind_yind(x_ind, y_ind)];

    for (const node_key of this[all_lcs_node_prop_all_node]) {
      /**
       * @type {lcs_node}
       */
      let child_node = this[node_key];

      if (this.crease_node_cb(cur_node, child_node)) {
        ObjectOnlyPushSet(
          cur_node,
          node_key,
          child_node,
          lcs_node_prop_child_node_arr
        );
      }
    }
  }

  /**
   * 默认 是按 次序 进行 罗列 最长 公共 子序列
   *
   * true 添加 该项 子序列
   *
   * false 不添加
   * @param {lcs_node} cur_node
   * @param {lcs_node} child_node
   * @returns {boolean}
   */
  crease_node_cb(cur_node, child_node) {
    if (
      cur_node.x_ind < child_node.x_ind &&
      cur_node.y_ind < child_node.y_ind
    ) {
      return true;
    }
    return false;
  }

  /**
   * 判定 当前 的 两个 值 是否 相等
   *
   * 可以自定义 判定函数 返回值 为 true 时 对 该值 进行 节点 计算
   * @param {any} x_source
   * @param {any} y_source
   * @returns {boolean}
   */
  value_same_cb(x_source, y_source) {
    if (x_source == y_source) {
      return true;
    }
    return false;
  }

  xOry_cb(x_source, y_source) {
    y_source;
    return x_source;
  }

  longest_length = 0;

  #get_longest_length() {
    let cur_length = 0;

    let i = 0;

    /**
     * @type {lcs_node}
     */
    let cur_child_node;

    do {
      cur_child_node = this[this[all_lcs_node_prop_all_node][i++]];

      cur_length = Math.max(cur_length, cur_child_node.deepth);
    } while (i < this[all_lcs_node_prop_all_node].length);
    return cur_length;
  }

  longest_squence_arr = [];

  /**
   * 获取 最长 子序列 集
   * @returns
   */
  #get_longest_squence_arr() {
    /**
     * @type {lcs_node[]}
     */
    let ret = [];

    /**
     * 递归 获取 完整的 最长链
     * @param {lcs_node} cur_node
     * @param {string[]} path
     * @param {number} deepth
     */
    let crease_path = (cur_node, path, deepth) => {
      if (deepth == 0) {
        ret.push(path);
        return;
      }

      /**
       * @type {lcs_node}
       */
      let cur_child_node;

      for (const node_key of cur_node.child_node_arr) {
        cur_child_node = cur_node[node_key];
        if (cur_child_node.deepth != deepth) {
          continue;
        }

        crease_path(
          cur_child_node,
          [...path, cur_child_node.value],
          deepth - 1
        );
      }
    };

    for (const node_key of this[all_lcs_node_prop_all_node]) {
      /**
       * @type {lcs_node}
       */
      let cur_node = this[node_key];
      if (cur_node.deepth == this.longest_length) {
        crease_path(cur_node, [cur_node.value], this.longest_length - 1);
      }
    }

    return ret;
  }
}

export { all_lcs_node, all_lcs_node_prop_all_node };
